package com.jdd.modules.parking.service.impl;

import cn.hutool.core.util.ObjectUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.jdd.common.constant.CameraRtspConstant;
import com.jdd.common.constant.JddConstant;
import com.jdd.common.util.DateUtil;
import com.jdd.common.util.SnowFlakeUtil;
import com.jdd.modules.configcartype.entity.ConfigCarType;
import com.jdd.modules.configcartype.service.IConfigCarTypeService;
import com.jdd.modules.operationLog.entity.ParkHandOverLog;
import com.jdd.modules.operationLog.mapper.ParkHandOverLogMapper;
import com.jdd.modules.order.entity.ParkOrderInfo;
import com.jdd.modules.order.service.IParkOrderInfoService;
import com.jdd.modules.parkcar.entity.ParkInnerInfo;
import com.jdd.modules.parkcar.mapper.ParkInnerListMapper;
import com.jdd.modules.parking.entity.*;
import com.jdd.modules.parking.mapper.ParkArmInfoMapper;
import com.jdd.modules.parking.mapper.ParkWorkStationMapper;
import com.jdd.modules.parking.service.IConfigParkInfoService;
import com.jdd.modules.parking.service.IParkArmInfoService;
import com.jdd.modules.parking.service.IparkCarEnterLogService;
import com.jdd.modules.parking.service.IparkCarOutLogService;
import com.jdd.modules.sdkidentifyresultpushlog.entity.ReChargeVo;
import com.jdd.modules.sdkidentifyresultpushlog.entity.SdkIdentifyResultPushLog;
import com.jdd.modules.system.util.MoblieControllerUtils;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.net.InetAddress;
import java.util.*;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutorService;

/**
 * @Description: 车场出入口配置
 * @Author: jdd
 * @Date: 2021-02-15
 * @Version: V1.0
 */
@Service
@Slf4j
public class ParkArmInfoServiceImpl extends ServiceImpl<ParkArmInfoMapper, ParkArmInfo> implements IParkArmInfoService {

    @Value("${spring.application.name}")
    private String applicationName;

    @Value("${server.port}")
    private String port;
    @Autowired
    private IparkCarEnterLogService parkCarEnterLogService;

    @Autowired
    private IParkOrderInfoService parkOrderInfoService;

    @Autowired
    private IparkCarOutLogService parkCarOutLogService;
    @Autowired
    ExecutorService executorService;

    @Resource
    private IConfigParkInfoService configParkInfoService;
    @Resource
    private IConfigCarTypeService carTypeService;
    @Resource
    private ParkArmInfoMapper parkArmInfoMapper;
    @Resource
    private ParkWorkStationMapper parkWorkStationMapper;
    @Resource
    private ParkHandOverLogMapper parkHandOverLogMapper;
    @Resource
    private ParkInnerListMapper parkInnerListMapper;

    @Override
    public ParkArmInfo findParkArmInfoBySerialNo(String serialNo) {
        LambdaQueryWrapper<ParkArmInfo> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(ParkArmInfo::getSerialNo, serialNo);
        queryWrapper.eq(ParkArmInfo::getArmStatus, "1");
        return parkArmInfoMapper.selectOne(queryWrapper);
    }

    @Override
    public ParkArmInfo findParkArmInfoByIp(String ip) {
        LambdaQueryWrapper<ParkArmInfo> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(ParkArmInfo::getIpAddr, ip);
        wrapper.eq(ParkArmInfo::getArmStatus, "1");
        return parkArmInfoMapper.selectOne(wrapper);
    }

    @Override
    public List<ParkArmInfo> findParkArmInfosByParkCode() {
        LambdaQueryWrapper<ParkArmInfo> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(ParkArmInfo::getArmStatus, "1");
        List<ParkArmInfo> list = this.list(queryWrapper);
        return list;
    }

    @Override
    public List<ParkArmInfo> findParkEnterArmInfos() {
        LambdaQueryWrapper<ParkArmInfo> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(ParkArmInfo::getArmStatus, 1);
        queryWrapper.eq(ParkArmInfo::getArmType, 0);
        return parkArmInfoMapper.selectList(queryWrapper);
    }

    @Override
    public Boolean removeByParkCode() {
        LambdaQueryWrapper<ParkArmInfo> queryWrapper = new LambdaQueryWrapper<>();
        if (parkArmInfoMapper.delete(queryWrapper) == 0) {
            return false;
        }
        return true;
    }

    @Override
    public boolean saveEntity(ParkArmInfo parkArmInfo) {
        return parkArmInfoMapper.insert(parkArmInfo) == 1;
    }

    @Override
    public boolean updateEntity(ParkArmInfo parkArmInfo) {
        return parkArmInfoMapper.updateById(parkArmInfo) == 1;
    }

    @Override
    public boolean deleteEntityById(String id) {
        return parkArmInfoMapper.deleteById(id) == 1;
    }

    @Override
    public boolean deleteEntityByIds(List<String> ids) {
        return parkArmInfoMapper.deleteBatchIds(ids) == 1;
    }

    @Override
    public Map<String, Object> getData(String workUser) {
        // 返回数据data
        Map data = new HashMap();
        // 车位信息
        CompletableFuture totalParkSpaceFuture = CompletableFuture.runAsync(() -> {

            // 总的停车位信息
            int totalParkSpace = configParkInfoService.lambdaQuery().list().size() > 0 ?
                    configParkInfoService.lambdaQuery().list().get(0).getTemporaryParking() : 0;
            // 未出场、场内临时车辆数量
            //临时车
//            int parkSpace = parkCarEnterLogService.lambdaQuery().eq(ParkCarEnterLog::getIsOut, "0").eq(ParkCarEnterLog::getCarNature, "0").count();
            QueryWrapper<ParkCarEnterLog> parkSpaceQueryWrapper = new QueryWrapper<>();
            parkSpaceQueryWrapper.select("DISTINCT(plate_no)");
            parkSpaceQueryWrapper.eq("is_out", "0");
            parkSpaceQueryWrapper.eq("car_nature", "0");
            int parkSpace = parkCarEnterLogService.count(parkSpaceQueryWrapper);

            //固定车，储值车,JddConstant.MqttRecResultPushLog.IS_WHITE_ONE是月租车
//            int parkSpaceLong = parkCarEnterLogService.lambdaQuery().eq(ParkCarEnterLog::getIsOut, "0").in(ParkCarEnterLog::getCarNature, JddConstant.StringNumber.STRING_ONE, JddConstant.StringNumber.STRING_TWO).count();
            QueryWrapper<ParkCarEnterLog> parkSpaceLongQueryWrapper = new QueryWrapper<>();
            parkSpaceLongQueryWrapper.select("DISTINCT(plate_no)");
            parkSpaceLongQueryWrapper.eq("is_out", "0");
            parkSpaceLongQueryWrapper.in("car_nature", Arrays.asList(JddConstant.StringNumber.STRING_ONE, JddConstant.StringNumber.STRING_TWO));
            int parkSpaceLong = parkCarEnterLogService.count(parkSpaceLongQueryWrapper);

            //其他车（警车，白名单）
//            int parkSpaceOther = parkCarEnterLogService.lambdaQuery().eq(ParkCarEnterLog::getIsOut, "0").in(ParkCarEnterLog::getCarNature, JddConstant.StringNumber.STRING_THREE, JddConstant.StringNumber.STRING_FOUR).count();
            QueryWrapper<ParkCarEnterLog> parkSpaceOtherQueryWrapper = new QueryWrapper<>();
            parkSpaceOtherQueryWrapper.select("DISTINCT(plate_no)");
            parkSpaceOtherQueryWrapper.eq("is_out", "0");
            parkSpaceOtherQueryWrapper.in("car_nature", Arrays.asList(JddConstant.StringNumber.STRING_THREE, JddConstant.StringNumber.STRING_FOUR));
            int parkSpaceOther = parkCarEnterLogService.count(parkSpaceOtherQueryWrapper);
            // 剩余车位数量
            int remainingPark = (totalParkSpace - parkSpace) < 0 ? 0 : (totalParkSpace - parkSpace);
            // 总停车位信息
            data.put("totalParkSpace", totalParkSpace);
            // 场内临时车数量
            data.put("parkSpace", parkSpace);
            // 场内固定车数量
            data.put("parkSpaceLong", parkSpaceLong);
            // 场内其他车数量
            data.put("parkSpaceOther", parkSpaceOther);
            // 剩余车位数量
            data.put("remainingPark", remainingPark);

        }, executorService);

        // 订单数量
        CompletableFuture countOrderFuture = CompletableFuture.runAsync(() -> {

            // 今日订单数量
            int countOrder =
                    parkOrderInfoService.lambdaQuery().eq(ParkOrderInfo::getOrderStatus, "2").ge(ParkOrderInfo::getCreateTime, DateUtil.getDay() + " 00:00:00").count();

            data.put("countOrder", countOrder);

        }, executorService);


        String finalWorkUser = workUser;
        CompletableFuture todayOrderSumFuture = CompletableFuture.runAsync(() -> {
            if (finalWorkUser != null) {
                QueryWrapper<ParkHandOverLog> wrapper = new QueryWrapper<>();
                wrapper.eq("operation_name", finalWorkUser);
                wrapper.orderByDesc("create_time");
                wrapper.isNull("hand_over_name");
                wrapper.last(" limit 1");
                ParkHandOverLog parkHandOverLog = parkHandOverLogMapper.selectOne(wrapper);
                if (parkHandOverLog != null) {
                    data.put("todayOrderSum", parkHandOverLog.getManualPaid());
                    data.put("HandOverLog", parkHandOverLog);
                }
            }

        }, executorService);


        CompletableFuture orderFuture = CompletableFuture.runAsync(() -> {
            // 订单信息
            List<ParkOrderInfo> parkOrderInfos =
                    parkOrderInfoService.lambdaQuery().eq(ParkOrderInfo::getOrderStatus, "2").orderByDesc(ParkOrderInfo::getCreateTime).last(" limit " +
                            "20").list();

            // 入场信息
            List<ParkCarEnterLog> parkCarEnterLogs = parkCarEnterLogService.lambdaQuery().orderByDesc(ParkCarEnterLog::getCreateTime).last(" " +
                    "limit 20").list();

            // 出场信息
            List<ParkCarOutLog> parkCarOutLogs =
                    parkCarOutLogService.lambdaQuery().orderByDesc(ParkCarOutLog::getCreateTime).last(" limit 20").list();

            // 场内信息
            List<ParkCarEnterLog> inParkinglotCar =
                    parkCarEnterLogService.lambdaQuery().eq(ParkCarEnterLog::getIsOut, JddConstant.StringNumber.STRING_ZERO)
                            .ne(ParkCarEnterLog::getIsTransfer, JddConstant.StringNumber.STRING_ONE).orderByDesc(ParkCarEnterLog::getCreateTime).last(" limit 20").list();


            data.put("parkOrderInfos", parkOrderInfos);
            data.put("parkCarEnterLogs", parkCarEnterLogs);
            data.put("parkCarOutLogs", parkCarOutLogs);
            data.put("inParkinglotCar", inParkinglotCar);

        }, executorService);

        try {
            CompletableFuture.allOf(totalParkSpaceFuture, countOrderFuture, todayOrderSumFuture, orderFuture).get();
        } catch (Exception e) {
            log.error("监控页面，线程执行异常：{}", e.getMessage());
        }

        return data;
    }

    @Override
    public boolean removeAllInfo() {
        try {
            parkArmInfoMapper.removeAllInfo();
        } catch (Exception e) {
            return false;
        }
        return true;
    }

    /*** 功能描述:生成入场记录
     * @param parkArmInfo
     * @param reChargeVo
     * @return:
     * @Author: lcy
     * @Date: 2021/9/26
     */
    @Override
    public ParkCarEnterLog getParkCarEnterLog(ParkArmInfo parkArmInfo, ReChargeVo reChargeVo, SdkIdentifyResultPushLog sdkIdentifyResultPushLog) {
        //生成入场记录
        ParkCarEnterLog carEnterLog = new ParkCarEnterLog();
        carEnterLog.setId(SnowFlakeUtil.getId());
        carEnterLog.setPlateNo(sdkIdentifyResultPushLog.getLicense());
        carEnterLog.setParkCode(parkArmInfo.getParkCode());
        carEnterLog.setParkName(parkArmInfo.getParkName());
        carEnterLog.setSerialNo(parkArmInfo.getSerialNo());
        carEnterLog.setArmName(parkArmInfo.getArmName());
        //前端传来的区域id，用户自行选择的区域id
        if (StringUtils.isNotBlank(parkArmInfo.getTargetAreaId())) {
            carEnterLog.setAreaId(parkArmInfo.getTargetAreaId());
        }
        //批次号
        carEnterLog.setBatchNumber(carEnterLog.getId());
        carEnterLog.setCarEnterTime(reChargeVo.getEnterDate());
        carEnterLog.setImgPath(sdkIdentifyResultPushLog.getImg());
        if (null != sdkIdentifyResultPushLog.getOssImg()) {
            carEnterLog.setOssImgPath(sdkIdentifyResultPushLog.getOssImg());
        }
        carEnterLog.setIsOut(JddConstant.IsOutType.ISOUT_ONE);
        carEnterLog.setIpAddr(sdkIdentifyResultPushLog.getIpAddr());
        carEnterLog.setCarTypeNo(String.valueOf(sdkIdentifyResultPushLog.getNType()));
        //如果是临时车需判断是否为内部车
        if (JddConstant.MqttRecResultPushLog.IS_WHITE_ZERO.equals(carEnterLog.getCarNature())) {
            ParkInnerInfo isInner = parkInnerListMapper.getIsInner(parkArmInfo.getParkCode(), sdkIdentifyResultPushLog.getLicense());
            if (isInner != null) {
                carEnterLog.setCarTypeNo(String.valueOf(JddConstant.TemporaryCarType.TEMPORARY_CAR_INNER));
            }
        }

        if (null != sdkIdentifyResultPushLog.getLicense() && (sdkIdentifyResultPushLog.getLicense().contains("无"))) {
            carEnterLog.setCarType(MoblieControllerUtils.NONEPLATE);
        } else {
            carEnterLog.setCarType(MoblieControllerUtils.HAVEPLATE);
        }

        carEnterLog.setCreateTime(new Date());

        // 新增字段
        carEnterLog.setThisArea(JddConstant.IntNumber.INT_ONE);
        carEnterLog.setEnterType(JddConstant.IntNumber.INT_ZERO);
        carEnterLog.setIsTransfer(JddConstant.StringNumber.STRING_ZERO);
        LambdaQueryWrapper<ConfigCarType> queryWrapper = Wrappers.lambdaQuery();
        queryWrapper.eq(ConfigCarType::getId, reChargeVo.getCarTypeId());
        List<ConfigCarType> carTypeList = carTypeService.list(queryWrapper);

        if (CollectionUtils.isNotEmpty(carTypeList)) {
            ConfigCarType configCarType = carTypeList.get(0);
            carEnterLog.setCarTypeId(configCarType.getId());
            carEnterLog.setCarTypeName(configCarType.getCarTypeName());
            carEnterLog.setCarNature(configCarType.getCarType());
        } else {
            LambdaQueryWrapper<ConfigCarType> newQueryWrapper = Wrappers.lambdaQuery();
            newQueryWrapper.isNotNull(ConfigCarType::getCarTypeNo);
            List<ConfigCarType> newCarTypeList = carTypeService.list(newQueryWrapper);
            newCarTypeList.forEach(carType -> {
                List<String> carTypeNos = Arrays.asList(carType.getCarTypeNo().split(","));
                if (carTypeNos.contains(String.valueOf(sdkIdentifyResultPushLog.getNType()))) {
                    carEnterLog.setCarTypeId(carType.getId());
                    carEnterLog.setCarTypeName(carType.getCarTypeName());
                    carEnterLog.setCarNature(carType.getCarType());
                }
            });
        }
        return carEnterLog;
    }

    /**
     * 根据相机类型获拼接频流rtsp地址
     *
     * @param
     * @return java.util.Map<java.lang.String, java.lang.String>
     * @author jdd孙庆伟
     * @date 2021/8/25 10:36:00
     * @version 1.0
     */
    @Override
    public String jointRtspUrl(ParkArmInfo parkArmInfo, ConfigParkInfo configParkInfo) {
        StringBuilder rtspSb = new StringBuilder();
        try {
            rtspSb.append(CameraRtspConstant.RESP);
            //相机ip
            rtspSb.append(parkArmInfo.getIpAddr());
            //芊熤
            if (JddConstant.CameraControl.CAMERA_QIANYI.equals(configParkInfo.getCameraType())) {
                rtspSb.append(CameraRtspConstant.QIANYICAMERA);
            }
            //华夏
            if (JddConstant.CameraControl.CAMERA_HUAXIA.equals(configParkInfo.getCameraType())) {
                rtspSb.append(CameraRtspConstant.HUAXIACAMERA);
            }
            //甄实
            if (JddConstant.CameraControl.CAMERA_ZHENSHI.equals(configParkInfo.getCameraType())) {
                rtspSb.append(CameraRtspConstant.ZHENSHI);
            }
        } catch (Exception e) {
            log.error("=========拼接车道视频流rtsp地址拼接错误:{}", e.getMessage());
            e.printStackTrace();
        }
        return rtspSb.toString();
    }

    /**
     * 根据相机类型拼接视频流video地址
     *
     * @param parkArmInfo
     * @param configParkInfo
     * @return java.lang.String
     * @author jdd孙庆伟
     * @date 2021/8/27 17:22:55
     * @version 1.0
     */
    @Override
    public String jointVideoUrl(ParkArmInfo parkArmInfo, ConfigParkInfo configParkInfo) {
        StringBuilder videoSb = new StringBuilder();
        try {
            videoSb.append(CameraRtspConstant.VIDEO);
            //臻识
            if (configParkInfo.getCameraType().equals("0")) {
                videoSb.append(parkArmInfo.getIpAddr());
                videoSb.append(":9080").append("/ws");
            } else {
                //获取本机ip
                InetAddress thisAddr = InetAddress.getLocalHost();
                String thisIp = thisAddr.getHostAddress();
                videoSb.append(thisIp).append(":");
                //当前端口号
                videoSb.append(port).append("/");
                //当前项目名称
                videoSb.append(applicationName);
                videoSb.append(CameraRtspConstant.WSSERVICE);
                videoSb.append(parkArmInfo.getSerialNo());
            }
        } catch (Exception e) {
            log.error("=========拼接车道视频流video地址拼接错误:{}", e.getMessage());
            e.printStackTrace();
        }
        return videoSb.toString();
    }

    @Override
    public ParkWorkStation getWorkFromUserId(String userId) {
        QueryWrapper<ParkArmInfo> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("used_user_id", userId);
        //当前userId对应的车道
        List<ParkArmInfo> armInfos = this.list(queryWrapper);
        if (ObjectUtil.isEmpty(armInfos)) {
            return null;
        }
        //车道对应的工作站
        ParkWorkStation workStation = parkWorkStationMapper.selectById(armInfos.get(0).getWorkStationId());
        if (ObjectUtil.isEmpty(workStation)) {
            return null;
        }
        return workStation;
    }

    @Override
    public List<ParkArmInfo> monitorArmList(String workName, String type) throws Exception {
        if (ObjectUtil.isEmpty(workName) || ObjectUtil.isEmpty(type)) {
            throw new Exception("参数不可为空！");
        }
        QueryWrapper<ParkArmInfo> wrapper = new QueryWrapper<>();
        wrapper.eq("work_station_name", workName);
        wrapper.eq("arm_type", type);
        return this.list(wrapper);
    }

    @Override
    public List<ParkArmInfo> workStationId(String workStationId) throws Exception {
        if (ObjectUtil.isEmpty(workStationId)) {
            throw new Exception("工作站id不可为空");
        }
        QueryWrapper<ParkArmInfo> wrapper = new QueryWrapper<>();
        wrapper.eq("work_station_id", workStationId);
        //只查已启用得
        wrapper.eq("arm_status", JddConstant.StringNumber.STRING_ONE);
        return this.list(wrapper);
    }

    @Override
    public boolean updateArmByWork(String id, String userId) {
        //校验参数
        if (ObjectUtil.isEmpty(id)) {
            log.error("----updateArmByWork方法参数为空");
            return false;
        }
        //校验参数取值
        ParkWorkStation station = parkWorkStationMapper.selectById(id);
        if (ObjectUtil.isEmpty(station)) {
            log.error("----updateArmByWork提示工作站没找到---");
            return false;
        }
        //校验没问题，查询车道列表
        QueryWrapper<ParkArmInfo> wrapper = new QueryWrapper<>();
        wrapper.eq("work_station_id", id);
        List<ParkArmInfo> armInfos = this.list(wrapper);
        //校验数据
        if (ObjectUtil.isEmpty(armInfos)) {
            log.error("----updateArmByWork提示车道为空----");
            return false;
        }
        //更改车道的used_user_id
        for (ParkArmInfo armInfo : armInfos) {
            armInfo.setUsedUserId(userId);
            //更新时清理缓存
            if (!this.updateEntity(armInfo)) {
                log.error("----updateArmByWork提示更新车道失败----");
                return false;
            }
        }
        return true;
    }

    /**
     * 获取识别类型对应的车辆类型
     */
    public ConfigCarType getCarType(String parkCode, int type) {
        QueryWrapper<ConfigCarType> configCarTypeQueryWrapper = new QueryWrapper<>();
        configCarTypeQueryWrapper.eq("park_code", parkCode);
        List<ConfigCarType> configCarTypeList = carTypeService.list(configCarTypeQueryWrapper);
        for (ConfigCarType configCarType : configCarTypeList) {
            configCarType.getCarTypeNo();
            List<String> list = Arrays.asList(configCarType.getCarTypeNo().split(","));
            if (list.contains(String.valueOf(type))) {
                return configCarType;
            }
        }
        return null;
    }
}
